<!-- 手势密码组件 -->
<template>
    <div class="gesture-lock">
        <canvas 
            ref="canvas" 
            width="480"
            height="480"
        ></canvas>
        <div v-for="(point, index) in points" 
            :key="index" 
            :class="['point', { active: currentPath.includes(index) }]" 
            :style="{ 
                top: point.y + 'px', 
                left: point.x + 'px',
                width: point.width + 'px',
                height: point.height + 'px'
            }"
            @touchstart.prevent="onTouchStart($event, index)"
            @touchmove.prevent="onTouchMove"
            @touchend.prevent="onTouchEnd">
            <div></div>
        </div>
    </div>
</template>

<script setup>
import { ref, onMounted, nextTick, onBeforeUnmount } from 'vue';

const props = defineProps({
    mode: {
        type: String,
        default: 'verify', // 'verify' 或 'set'
        validator: (value) => ['verify', 'set'].includes(value)
    }
});

const emit = defineEmits(['verify', 'set', 'error']);

// Canvas相关引用
const canvas = ref(null);
let context = null;

// 九宫格坐标
const points = ref([
    { x: 48, y: 48, width: 96, height: 96 }, { x: 240, y: 48, width: 96, height: 96 }, { x: 432, y: 48, width: 96, height: 96 },
    { x: 48, y: 240, width: 96, height: 96 }, { x: 240, y: 240, width: 96, height: 96 }, { x: 432, y: 240, width: 96, height: 96 },
    { x: 48, y: 432, width: 96, height: 96 }, { x: 240, y: 432, width: 96, height: 96 }, { x: 432, y: 432, width: 96, height: 96 }
]);

// 手势状态管理
const currentPath = ref([]);
const isDrawing = ref(false);
const firstPassword = ref([]); // 第一次输入的密码
const isFirstInput = ref(true); // 是否是第一次输入

// 初始化画布
async function initCanvas() {
    await nextTick();
    if (!canvas.value) return;
    
    context = canvas.value.getContext('2d');
    canvas.value.width = 480;
    canvas.value.height = 480;
    drawGrid();
}

// 组件生命周期钩子
onMounted(initCanvas);
onBeforeUnmount(() => {
    window.removeEventListener('resize', initCanvas);
});

// 绘制网格
function drawGrid() {
    if (!context || !canvas.value) return;
    
    context.clearRect(0, 0, canvas.value.width, canvas.value.height);
    
    points.value.forEach((p) => {
        context.beginPath();
        context.arc(p.x, p.y, 15, 0, Math.PI * 2);
        context.fillStyle = '#fff';
        context.fill();
        context.strokeStyle = '#ccc';
        context.lineWidth = 2;
        context.stroke();
    });
}

// 绘制连接线
function drawConnection(toIndex) {
    if (!context || !canvas.value || currentPath.value.length === 0) return;
    
    context.clearRect(0, 0, canvas.value.width, canvas.value.height);
    drawGrid();
    
    for (let i = 0; i < currentPath.value.length - 1; i++) {
        const from = points.value[currentPath.value[i]];
        const to = points.value[currentPath.value[i + 1]];
        
        context.beginPath();
        context.moveTo(from.x, from.y);
        context.lineTo(to.x, to.y);
        context.strokeStyle = '#fb8fae';
        context.lineWidth = 2;
        context.stroke();
    }
    
    const from = points.value[currentPath.value[currentPath.value.length - 2] || currentPath.value[0]];
    const to = points.value[toIndex];
    
    context.beginPath();
    context.moveTo(from.x, from.y);
    context.lineTo(to.x, to.y);
    context.strokeStyle = '#fb8fae';
    context.lineWidth = 2;
    context.stroke();
}

// 获取最近的点的索引
function getNearestPoint(x, y) {
    if (!canvas.value) return -1;
    
    const rect = canvas.value.getBoundingClientRect();
    const relativeX = x - rect.left;
    const relativeY = y - rect.top;
    
    let closestIndex = -1;
    
    points.value.forEach((p, i) => {
        if (relativeX >= p.x - p.width/2 && 
            relativeX <= p.x + p.width/2 && 
            relativeY >= p.y - p.height/2 && 
            relativeY <= p.y + p.height/2) {
            closestIndex = i;
        }
    });
    
    return closestIndex;
}

// 触摸事件处理
function onTouchStart(e, index) {
    e.preventDefault();
    e.stopPropagation();
    if (!currentPath.value.includes(index)) {
        currentPath.value = [index];
        isDrawing.value = true;
        drawConnection(index);
    }
}

// 节流处理
let lastMoveTime = 0;
const throttleDelay = 16; // 约60fps

function onTouchMove(e) {
    e.preventDefault();
    e.stopPropagation();
    
    const now = Date.now();
    if (!isDrawing.value || now - lastMoveTime < throttleDelay) return;
    
    lastMoveTime = now;
    const touch = e.touches[0];
    const nextPoint = getNearestPoint(touch.clientX, touch.clientY);
    
    if (nextPoint !== -1 && !currentPath.value.includes(nextPoint)) {
        currentPath.value.push(nextPoint);
        drawConnection(nextPoint);
    }
}

function onTouchEnd(e) {
    e.preventDefault();
    e.stopPropagation();
    if (!isDrawing.value) return;
    isDrawing.value = false;
    
    if (currentPath.value.length > 0) {
        if (props.mode === 'verify') {
            verifyPassword();
        } else {
            checkPassword();
        }
        setTimeout(() => {
            currentPath.value = [];
            drawGrid();
        }, 100);
    }
}

// 验证密码
function verifyPassword() {
    if (currentPath.value.length < 4) {
        emit('error', '最少连接四个点，请重新输入！');
        return;
    }

    const storedPassword = JSON.parse(localStorage.getItem('gesturePassword') || '[]');
    const isMatch = currentPath.value.length === storedPassword.length &&
        currentPath.value.every((point, index) => point === storedPassword[index]);
    
    if (isMatch) {
        emit('verify', true);
    } else {
        emit('error', '手势密码错误，请重试！');
    }
}

// 检查密码
function checkPassword() {
    if (currentPath.value.length < 4) {
        emit('error', '最少连接四个点，请重新输入！');
        return;
    }

    if (isFirstInput.value) {
        firstPassword.value = [...currentPath.value];
        isFirstInput.value = false;
        emit('set', { status: 'confirm' });
    } else {
        const isMatch = currentPath.value.length === firstPassword.value.length &&
            currentPath.value.every((point, index) => point === firstPassword.value[index]);
        
        if (isMatch) {
            emit('set', { status: 'success', password: firstPassword.value });
        } else {
            isFirstInput.value = true;
            firstPassword.value = [];
            emit('error', '与上一次绘制不一致，请从新绘制！');
        }
    }
}
</script>

<style lang='less' scoped>
.gesture-lock {
    position: relative;
    width: 480px;
    height: 480px;
    border-radius: 24px;
    overflow: hidden;
    margin: 40px auto;
}

canvas {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    z-index: 1;
}

.point {
    position: absolute;
    border-radius: 50%;
    background: #fff;
    border: 2px solid black;
    text-align: center;
    line-height: 96px;
    font-size: 48px;
    user-select: none;
    transition: all 0.2s ease;
    transform: translate(-50%, -50%);
    z-index: 2;
    div {
        width: 20px;
        height: 20px;
        border-radius: 100%;
        background-color: gray;
        margin: 0 auto;
        margin-top: 38px;
    }
}

.point.active {
    background: radial-gradient(circle at center, #fb8fae, #fb8fae);
    border: none;
    transform: translate(-50%, -50%);
    div {
        background-color: aqua;
    }
}
</style> 